/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// TGUI - Texus' Graphical User Interface
// Copyright (C) 2012-2025 Bruno Van de Velde (vdv_b@tgui.eu)
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented;
//    you must not claim that you wrote the original software.
//    If you use this software in a product, an acknowledgment
//    in the product documentation would be appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such,
//    and must not be misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#ifndef TGUI_LIST_VIEW_HPP
#define TGUI_LIST_VIEW_HPP

#include <TGUI/Widgets/Scrollbar.hpp>
#include <TGUI/Renderers/ListViewRenderer.hpp>
#include <TGUI/Text.hpp>

#include <set>
#include <limits>

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace tgui
{
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    /// @brief List view widget
    ///
    /// When no columns are added, the list view acts like a ListBox with a few extra functionalities.
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class TGUI_API ListView : public Widget, public DualScrollbarChildInterface
    {
      public:

        using Ptr = std::shared_ptr<ListView>; //!< Shared widget pointer
        using ConstPtr = std::shared_ptr<const ListView>; //!< Shared constant widget pointer

        static constexpr const char StaticWidgetType[] = "ListView"; //!< Type name of the widget

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief The text alignment for all texts within a column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        using ColumnAlignment TGUI_DEPRECATED("Use tgui::HorizontalAlignment instead") = HorizontalAlignment;

        struct Item
        {
            std::vector<Text> texts;
            Any data;
            Sprite icon;
        };

        struct Column
        {
            float width = 0;
            float designWidth = 0;
            float widestItemWidth = 0;
            std::size_t widestItemIndex = std::numeric_limits<std::size_t>::max();
            Text text;
            HorizontalAlignment alignment = HorizontalAlignment::Left;
            bool autoResize = false;
            bool expanded = false;
        };

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /// @brief Constructor
        /// @param typeName     Type of the widget
        /// @param initRenderer Should the renderer be initialized? Should be true unless a derived class initializes it.
        /// @see create
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        ListView(const char* typeName = StaticWidgetType, bool initRenderer = true);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Creates a new list view widget
        /// @return The new list view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static ListView::Ptr create();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Makes a copy of another list view
        ///
        /// @param listView  The other list view
        ///
        /// @return The new list view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD static ListView::Ptr copy(const ListView::ConstPtr& listView);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer that may be shared with other widgets using the same renderer
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ListViewRenderer* getSharedRenderer() override;
        TGUI_NODISCARD const ListViewRenderer* getSharedRenderer() const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the renderer, which gives access to functions that determine how the widget is displayed
        /// @return Temporary pointer to the renderer
        /// @warning After calling this function, the widget has its own copy of the renderer and it will no longer be shared.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD ListViewRenderer* getRenderer() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the size of the list view
        ///
        /// @param size  The new size of the list view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSize(const Layout2d& size) override;
        using Widget::setSize;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Adds a column
        ///
        /// @param text      The caption of the new column
        /// @param width     Width of the column. Set width to 0 to make it depend on the width of the column caption.
        /// @param alignment The text alignment for all texts in the column
        ///
        /// @return Index of the added column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        std::size_t addColumn(const String& text, float width = 0, HorizontalAlignment alignment = HorizontalAlignment::Left);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text of a column
        ///
        /// @param index  Index of the column to change
        /// @param text   Caption of the column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setColumnText(std::size_t index, const String& text);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the text of a column
        ///
        /// @param index  Index of the column
        ///
        /// @return Caption of the column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD String getColumnText(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the width of a column
        ///
        /// @param index  Index of the column to change
        /// @param width  Width of the column. Set width to 0 to make it depend on the width of the column caption.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setColumnWidth(std::size_t index, float width);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the width of a column
        ///
        /// @param index  Index of the column
        ///
        /// @return Current width of the column
        ///
        /// @warning This returns the actual width of the column, which may differ from the value passed to
        ///          addColumn or setColumnWidth when e.g. the width was set to 0.
        ///
        /// @see getColumnDesignWidth
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getColumnWidth(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the original width of a column (the value passed to addColumn or the last setColumnWidth call)
        ///
        /// @param index  Index of the column
        ///
        /// @return Width of the column prior to expanding the column and prior to the user resizing it
        ///
        /// @see getColumnWidth
        ///
        /// @since TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getColumnDesignWidth(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text alignment within a column
        ///
        /// @param columnIndex Index of the column to change
        /// @param alignment   The text alignment for all texts in the column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setColumnAlignment(std::size_t columnIndex, HorizontalAlignment alignment);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the current text alignment within a column
        ///
        /// @param columnIndex Index of the column to inspect
        ///
        /// @return Text alignment for all texts in the column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD HorizontalAlignment getColumnAlignment(std::size_t columnIndex) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the column width depends on the widest item added to that column
        ///
        /// @param index      Index of the column to change
        /// @param autoResize Should the column's width expand and shrink when the widest item in the column changes?
        ///
        /// If auto-resize is true then the width specified in addColumn and setColumnWidth will act as the minimum width.
        /// If the width was set to 0 then the minimum width is the width of the header caption.
        ///
        /// If the user was allowed to resize columns (with setResizableColumns) then the auto-resize will be set to false again
        /// as soon as the user manually resizes the column.
        ///
        /// @since TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setColumnAutoResize(std::size_t index, bool autoResize);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the column width depends on the widest item added to that column
        ///
        /// @param index  Index of the column
        ///
        /// @return Does the column's width expand and shrink when the widest item in the column changes?
        ///
        /// @since TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getColumnAutoResize(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether a column is expanded to fill the list view (if all columns fit inside the list view)
        ///
        /// @param index  Index of the column to change
        /// @param expand Should the column's width expand to make it fill the list view when the list view isn't full yet?
        ///
        /// If multiple columns are marked as expanded, the free remaining width will be divided between them.
        ///
        /// If auto-resize is also set to true then the width determine by auto-resize is considered the minimum width of the column.
        ///
        /// If the user was allowed to resize columns (with setResizableColumns) then the column will no longer be expanded
        /// after the user manually resizes the column. Note that the user can't resize the last column while at least one column
        /// is still marked as expanded.
        ///
        /// @since TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setColumnExpanded(std::size_t index, bool expand);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the column is expanded to fill the list view (if all columns fit inside the list view)
        ///
        /// @param index  Index of the column
        ///
        /// @return Is the column is expanded to make it fill the list view when the list view isn't full yet?
        ///
        /// @since TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getColumnExpanded(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Removes all columns
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void removeAllColumns();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the amount of columns in the list view
        ///
        /// @return Number of columns
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::size_t getColumnCount() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the height of the header row
        ///
        /// @param height  Height of the header or 0 to make the header size depend on the row height
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setHeaderHeight(float height);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the height of the header row
        ///
        /// @return Configured height of the header
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getHeaderHeight() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the height of the header or 0 if no header row is shown
        ///
        /// @return getHeaderHeight() + getHeaderSeparatorHeight() or 0 if no header is shown
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getCurrentHeaderHeight() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the header is shown
        ///
        /// @param showHeader  Whether the header containing the column names shoud be visible
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setHeaderVisible(bool showHeader);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the header is shown
        ///
        /// @return Whether the header containing the column names is visible
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getHeaderVisible() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Adds an item to the list
        ///
        /// @param text  The caption of the item you want to add
        ///
        /// @return Index of the item that was just added
        ///
        /// This function only sets a value for the first column.
        /// To add an item with values for multiple columns, see the addItem(std::vector<tgui::String>) function.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        std::size_t addItem(const String& text);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Adds an item with values for multiple columns to the list
        ///
        /// @param item  Texts for each column
        ///
        /// @return Index of the item that was just added
        ///
        /// Example usage:
        /// @code
        /// listView->addItem({"DejaVuSans.ttf", "720.9 KB", "01 Jan 1970"});
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        std::size_t addItem(const std::vector<String>& item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Adds multiple items to the list
        ///
        /// @param items  List of items that should be passed to the addItem
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void addMultipleItems(const std::vector<std::vector<String>>& items);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Inserts an item into the list
        ///
        /// @param index Index to insert the item at
        /// @param text The caption of the item you want to add
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void insertItem(std::size_t index, const String& text);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Inserts an item into the list
        ///
        /// @param index Index to insert the item at
        /// @param item Texts for each column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void insertItem(std::size_t index, const std::vector<String>& item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Inserts multiple items into the list
        ///
        /// @param index Index to insert the items at
        /// @param items List of items that should be passed to the insertItem function
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void insertMultipleItems(std::size_t index, const std::vector<std::vector<String>>& items);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes an item with values for multiple columns to the list
        ///
        /// @param index Index of the item to update
        /// @param item  Texts for each column
        ///
        /// @return True when the item was updated, false when the index was too high
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool changeItem(std::size_t index, const std::vector<String>& item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the caption of a single value in the item
        ///
        /// @param index  Index of the item to update
        /// @param column Index of the column to change
        /// @param item   Texts for the given column for the given item
        ///
        /// @return True when the item was updated, false when the index was too high
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool changeSubItem(std::size_t index, std::size_t column, const String& item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Removes the item from the list view
        ///
        /// @param index  Index of the item in the list view
        ///
        /// @return True when the item was removed, false when the index was too high
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool removeItem(std::size_t index);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Removes all items from the list
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void removeAllItems();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Selects an item in the list view
        ///
        /// @param index  Index of the item in the list view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSelectedItem(std::size_t index);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Selects items in the list view
        ///
        /// @param indices  Indices of the items in the list view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSelectedItems(const std::set<std::size_t>& indices);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Deselects the selected items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void deselectItems();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Gets the index of the selected item
        ///
        /// @return The index of the selected item, or -1 when no item was selected
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD int getSelectedItemIndex() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Gets the index of the item below the mouse cursor
        /// @return The index of the item below the mouse, or -1 when the mouse isn't on top of any item
        /// @since TGUI 1.5
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD int getHoveredItemIndex() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Gets the indices of the selected items
        ///
        /// @return The indices of the selected items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::set<std::size_t> getSelectedItemIndices() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Allow multi selection of the items
        ///
        /// @param multiSelect  Whether several items can be selected
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setMultiSelect(bool multiSelect);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns multi selection of the items is allowed
        ///
        /// @return Whether several items can be selected
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getMultiSelect() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Store some user data with the item
        ///
        /// @param index Index of the item
        /// @param data  User data to store
        ///
        /// Examples:
        /// @code
        /// listView->setItemData(idx, "Data"); // Note: type to retrieve with getItemData is 'const char*' here
        /// listView->setItemData(idx, 5);
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setItemData(std::size_t index, Any data);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns user data stored in the item
        /// @return Stored data
        /// @throw std::bad_cast if the template type does not match the type inside the std::any variable passed in setItemData
        ///        or when the index was too high (which acts as if you access an empty std::any variable).
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        template <typename DataType>
        TGUI_NODISCARD DataType getItemData(std::size_t index) const
        {
            if (index < m_items.size())
                return AnyCast<DataType>(m_items[index].data);
            else
                throw std::bad_cast();
        }

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets a small icon in front of the item
        ///
        /// @param index   Index of the item
        /// @param texture Texture of the item icon
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setItemIcon(std::size_t index, const Texture& texture);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Gets the icon displayed in front of the item
        ///
        /// @param index  Index of the item
        ///
        /// @return Texture of the item icon
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Texture getItemIcon(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the amount of items in the list view
        ///
        /// @return Number of items inside the list view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::size_t getItemCount() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Retrieves an item in the list
        ///
        /// @param index  The index of the item
        ///
        /// @return Text of the item or an empty string when the index was higher than the amount of items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD String getItem(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Retrieves the values of all columns for an item in the list
        ///
        /// @param index  The index of the item
        ///
        /// @return Texts of the item for each column or an list of empty strings when the index was too high
        ///
        /// The returned list has the same length as the amount of columns.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::vector<String> getItemRow(std::size_t index) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Retrieves the value for a cell in the list
        ///
        /// @param rowIndex The index of the row
        /// @param columnIndex The index of the column
        ///
        /// @return Text of the cell or an empty string when the index is out of range
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD String getItemCell(std::size_t rowIndex, std::size_t columnIndex) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns a list of the texts in the first column for all items in the list view
        ///
        /// @return Texts of the first column of items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::vector<String> getItems() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns a list of all column values for all items in the list view
        ///
        /// @return Texts of the items and their subitems
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::vector<std::vector<String>> getItemRows() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sort items
        ///
        /// @param index The index of the column for sorting
        /// @param cmp   The comparator
        ///
        /// Example:
        /// @code
        /// listView->sort(0, [](const tgui::String& a, const tgui::String& b) { return a < b; });
        /// @endcode
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void sort(std::size_t index, const std::function<bool(const String&, const String&)>& cmp);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the height of the items in the list view
        ///
        /// @param itemHeight  The size of a single item in the list
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setItemHeight(unsigned int itemHeight);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the height of the items in the list view
        ///
        /// @return The item height
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getItemHeight() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the text size of the header caption
        ///
        /// @param textSize  The character size of the header text
        ///
        /// By default, header text size is the same as the text size of the items.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setHeaderTextSize(unsigned int textSize);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the text size of the header caption
        ///
        /// @return The header text size
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getHeaderTextSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the width of the column separator
        ///
        /// @param width  Width of the line separating the columns
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setSeparatorWidth(unsigned int width);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the width of the column separator
        ///
        /// @return Width of the line separating the columns
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getSeparatorWidth() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the height of the separator between the header and the items
        ///
        /// @param height  Height of the line below the header
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setHeaderSeparatorHeight(unsigned int height);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the height of the separator between the header and the items
        ///
        /// @return Height of the line below the header
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getHeaderSeparatorHeight() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the width of the grid lines
        ///
        /// @param width  Width of the grid lines
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setGridLinesWidth(unsigned int width);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the width of the grid lines
        ///
        /// @return Width of the grid lines
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getGridLinesWidth() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the list view scrolls to the bottom when a new item is added
        ///
        /// @param autoScroll  Should list view scroll to the bottom when new items are added?
        ///
        /// Auto scrolling is enabled by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setAutoScroll(bool autoScroll);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the list view scrolls to the bottom when a new item is added
        ///
        /// @return Does the list view scroll to the bottom when new items are added
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getAutoScroll() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether lines are drawn between columns
        ///
        /// @param showGridLines  Is there a vertical line to separate columns?
        ///
        /// Vertical grid lines are shown by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setShowVerticalGridLines(bool showGridLines);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether lines are drawn between items
        ///
        /// @return Is there a horizontal line to separate items?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getShowVerticalGridLines() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether lines are drawn between items
        ///
        /// @param showGridLines  Is there a horizontal line to separate items?
        ///
        /// Horizontal grid lines are hidden by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setShowHorizontalGridLines(bool showGridLines);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether lines are drawn between items
        ///
        /// @return Is there a horizontal line to separate items?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getShowHorizontalGridLines() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the last column is expanded to fill the list view (if all columns fit inside the list view)
        ///
        /// @param expand  Make the last column larger to make it fill the list view when it isn't full yet?
        ///
        /// @deprecated Replaced by setColumnAutoExpanded and setColumnAutoResize in TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use setColumnAutoExpanded and setColumnAutoResize instead") void setExpandLastColumn(bool expand);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the last column is expanded to fill the list view (if all columns fit inside the list view)
        ///
        /// @return Is the last column larger to make it fill the list view when the list view isn't full yet?
        ///
        /// @deprecated Replaced by ColumnAutoExpanded and ColumnAutoResize setters and getters in TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use ColumnAutoExpanded and ColumnAutoResize setters and getters instead") TGUI_NODISCARD bool getExpandLastColumn() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes when the vertical scrollbar should be displayed
        /// @param policy  The policy for displaying the vertical scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->setPolicy(policy) instead") void setVerticalScrollbarPolicy(Scrollbar::Policy policy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns when the vertical scrollbar should be displayed
        /// @return The policy for displaying the vertical scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->getPolicy() instead") TGUI_NODISCARD Scrollbar::Policy getVerticalScrollbarPolicy() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes when the horizontal scrollbar should be displayed
        /// @param policy  The policy for displaying the horizontal scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->setPolicy(policy) instead") void setHorizontalScrollbarPolicy(Scrollbar::Policy policy);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns when the horizontal scrollbar should be displayed
        /// @return The policy for displaying the horizontal scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->getPolicy() instead") TGUI_NODISCARD Scrollbar::Policy getHorizontalScrollbarPolicy() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the thumb position of the vertical scrollbar
        ///
        /// @param value  New value of the vertical scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->setValue(value) instead") void setVerticalScrollbarValue(unsigned int value);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the thumb position of the vertical scrollbar
        ///
        /// @return Value of the vertical scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->getValue() instead") TGUI_NODISCARD unsigned int getVerticalScrollbarValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the maximum thumb position of the vertical scrollbar
        ///
        /// @return Maximum value of the vertical scrollbar
        ///
        /// @since TGUI 1.4
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getVerticalScrollbar()->getMaxValue() instead") TGUI_NODISCARD unsigned int getVerticalScrollbarMaxValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes the thumb position of the horizontal scrollbar
        ///
        /// @param value  New value of the horizontal scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->setValue(value) instead") void setHorizontalScrollbarValue(unsigned int value);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the thumb position of the horizontal scrollbar
        ///
        /// @return Value of the horizontal scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->getValue() instead") TGUI_NODISCARD unsigned int getHorizontalScrollbarValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns the maximum thumb position of the horizontal scrollbar
        ///
        /// @return Maximum value of the horizontal scrollbar
        ///
        /// @since TGUI 1.4
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_DEPRECATED("Use getHorizontalScrollbar()->getMaxValue() instead") TGUI_NODISCARD unsigned int getHorizontalScrollbarMaxValue() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Sets a size to which all icons should be scaled
        ///
        /// @param iconSize  Wanted size for all icons
        ///
        /// By default the fixed icon size is set to (0,0) which doesn't scale the icons and lets each icon use the texture size.
        /// By setting only the x or y component of the vector to 0, that dimension will be calculated to keep the icon ratio.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setFixedIconSize(Vector2f iconSize);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns to which size all icons should be scaled
        ///
        /// @return Wanted size for all icons
        ///
        /// By default the fixed icon size is set to (0,0) which doesn't scale the icons and lets each icon use the texture size.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Vector2f getFixedIconSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Changes whether the user can resize the columns by dragging the border between columns
        ///
        /// @param resizable  Can columns be resized by dragging their border?
        ///
        /// Columns have a fixed width and are not resizable by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void setResizableColumns(bool resizable);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the user can resize the columns by dragging the border between columns
        ///
        /// @return Can columns be resized by dragging their border?
        ///
        /// Columns have a fixed width and are not resizable by default.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool getResizableColumns() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Returns whether the mouse position (which is relative to the parent widget) lies on top of the widget
        /// @return Is the mouse on top of the widget?
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool isMouseOnWidget(Vector2f pos) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool leftMousePressed(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void leftMouseReleased(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void rightMousePressed(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void mouseMoved(Vector2f pos) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool scrolled(float delta, Vector2f pos, bool touch) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void mouseNoLongerOnWidget() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void leftMouseButtonNoLongerDown() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @internal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void keyPressed(const Event::KeyEvent& event) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called by the parent of the widget to check if keyPressed would process the event
        ///
        /// @param event  Key event that took place
        ///
        /// @return True if the event would be handled by the widget, false if the key event doesn't affect the widget
        ///
        /// @since TGUI 1.1
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool canHandleKeyPress(const Event::KeyEvent& event) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Draw the widget to a render target
        ///
        /// @param target Render target to draw to
        /// @param states Current render states
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void draw(BackendRenderTarget& target, RenderStates states) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Retrieves a signal based on its name
        ///
        /// @param signalName  Name of the signal
        ///
        /// @return Signal that corresponds to the name
        ///
        /// @throw Exception when the name does not match any signal
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Signal& getSignal(String signalName) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Function called when one of the properties of the renderer is changed
        ///
        /// @param property  Name of the property that was changed
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void rendererChanged(const String& property) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Saves the widget as a tree node in order to save it to a file
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD std::unique_ptr<DataIO::Node> save(SavingRenderersMap& renderers) const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Loads the widget from a tree of nodes
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void load(const std::unique_ptr<DataIO::Node>& node, const LoadingRenderersMap& renderers) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function is called when the mouse enters the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void mouseEnteredWidget() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief This function is called when the mouse leaves the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void mouseLeftWidget() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the text size is changed (either by setTextSize or via the renderer)
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateTextSize() override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        /// @brief Called when the policy of one of the scrollbars has been changed calling either
        ///        getVerticalScrollbar()->setPolicy(...) or getHorizontalScrollbar()->setPolicy(...)
        ///
        /// @param orientation  Vertical or Horizontal depending on which scrollbar triggered the callback
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void scrollbarPolicyChanged(Orientation orientation) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Returns the size without the borders
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Vector2f getInnerSize() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Create a Text object for an item from the given caption, using the preset color, font, text size and opacity
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Text createText(const String& caption);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Create a Text object for a header text from the given caption, using the preset color, font, text size and opacity
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Text createHeaderText(const String& caption);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Changes the color of all Text objects in an item
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        virtual void setItemColor(std::size_t index, const Color& color);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Calculate the width of the column based on its caption when no column width was provided
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float calculateAutoColumnWidth(const Text& text);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Update the colors of the selected and hovered items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateSelectedAndhoveredItemColors();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Update the color of all the items
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateItemColors();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Changes the color of all header texts
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateHeaderTextsColor();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Update on which item the mouse is standing
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateHoveredItem(int item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Update which item is selected
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateSelectedItem(int item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Selects multiple items when multi-select is on and the user selects an item while the shift key is held down
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void selectRangeFromEvent(std::size_t item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates which item is the widest in the given column, by calculating the width of each item
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool updateWidestItemInColumn(std::size_t columnIndex);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates which item is the widest in the given column, when only one item has changed in width
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool updateWidestItemInColumn(std::size_t columnIndex, std::size_t itemIndex);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates which item is the widest in all columns, by calculating the width of each item
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool updateWidestItem();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Updates which item is the widest in all columns, when only one item has changed in width
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool updateWidestItem(std::size_t itemIndex);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // This function needs to be called when items are inserted into the list. If the widest item for each column is located
        // below the inserted item then it's index needs to be updated.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void incrementWidestItemIndices(std::size_t itemIndex);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Recalculates the width of each column if they don't have a fixed width. For auto-resized columns, the widest item
        // should have already been updated in each column before this function is called.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateColumnWidths();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Returns whether at least one column potentially has its width expanded to fill the list view
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool hasExpandedColumn() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Add item to selected set
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void addSelectedItem(int item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Remove item from selected set
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void removeSelectedItem(std::size_t item);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Update on which item the mouse is standing, given the current mouse position
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateHoveredItemByMousePos(Vector2f mousePos);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Returns either the configured separator width or the width of vertical grid lines, whichever is larger.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD unsigned int getTotalSeparatorWidth() const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Returns the total width an Item takes up at some column, assuming it will not be cut off by the column.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD float getItemTotalWidth(const Item& item, std::size_t columnIndex) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Found out which column is located below the mouse. The mouseLeft is relative to the widget position.
        // This function should only be called after checking that the mouse is positioned on top of the header.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD int getColumnIndexBelowMouse(float mouseLeft);

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Returns whether the mouse is standing between two columns.
        // If true then columnIndex is set to the index of border below the mouse (1 is the border between first two columns).
        // If false then columnIndex remains unchanged.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD bool findBorderBelowMouse(Vector2f pos, std::size_t& columnIndex) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Recalculate the size and viewport size of the scrollbars
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateScrollbars();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Recalculate the maximum value for the vertical scrollbar
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void updateVerticalScrollbarMaximum();

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Draw the header text for a single column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void drawHeaderText(BackendRenderTarget& target, RenderStates states, float columnWidth, float headerHeight, std::size_t column) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Draw the texts in a single column
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        void drawColumn(BackendRenderTarget& target, RenderStates states, std::size_t firstItem, std::size_t lastItem, std::size_t column, float columnWidth) const;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // This function is called every frame with the time passed since the last frame.
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        bool updateTime(Duration elapsedTime) override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        // Makes a copy of the widget
        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
        TGUI_NODISCARD Widget::Ptr clone() const override;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    public:

        /// An item was selected in the list view
        /// Optional parameter: selected item index (-1 when deselecting)
        ///
        /// Note that when MultiSelect is true, this signal is triggered for every change to the selected items. The optional
        /// parameter will contain the lowest index in the selected items or -1 when none of the items are selected.
        SignalInt onItemSelect  = {"ItemSelected"};

        SignalInt onDoubleClick = {"DoubleClicked"};  //!< An item was double clicked. Optional parameter: selected item index
        SignalInt onRightClick  = {"RightClicked"};   //!< Right mouse pressed. Optional parameter: index of item below mouse (-1 when not on top of item)
        SignalInt onHeaderClick = {"HeaderClicked"};  //!< The header was clicked. Optional parameter: column index

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    protected:

        std::vector<Column> m_columns;
        std::vector<Item>   m_items;
        std::set<std::size_t> m_selectedItems;

        int m_hoveredItem = -1;
        int m_firstSelectedItemIndex = -1;
        int m_focusedItemIndex = -1;

        float m_requestedHeaderHeight = 0;
        unsigned int m_itemHeight = 0;
        unsigned int m_headerTextSize = 0;
        unsigned int m_headerSeparatorHeight = 0;
        unsigned int m_separatorWidth = 1;
        unsigned int m_gridLinesWidth = 1;
        unsigned int m_iconCount = 0;
        float m_maxIconWidth = 0;
        float m_widestItemWidth = 0; // If there are no columns, this is the maximum width from all items
        std::size_t m_widestItemIndex = std::numeric_limits<std::size_t>::max(); // If there are no columns, this is the index of the item with the maximum width
        bool m_headerVisible = true;
        bool m_showHorizontalGridLines = false;
        bool m_showVerticalGridLines = true;
        bool m_expandLastColumn = false; // TGUI_NEXT: Remove this property
        bool m_multiSelect = false;
        bool m_resizableColumns = false;
        Vector2f m_fixedIconSize;
        Cursor::Type m_currentListViewMouseCursor = Cursor::Type::Arrow;
        std::size_t m_resizingColumn = 0;
        float m_resizingColumnLastMouseX = 0;

        int m_mouseOnHeaderIndex = -1; // If the left mouse is down, this contains the index of the column if the mouse went down on the header
        int m_possibleDoubleClick = false; // Will be set to true after the first click, but gets reset to false when the second click does not occur soon after
        bool m_autoScroll = true; // Should the list view scroll to the bottom when a new item is added?

        Sprite m_spriteHeaderBackground;
        Sprite m_spriteBackground;

        // Cached renderer properties
        Borders   m_bordersCached;
        Borders   m_paddingCached;
        Color     m_borderColorCached;
        Color     m_separatorColorCached;
        Color     m_gridLinesColorCached;
        Color     m_headerTextColorCached;
        Color     m_headerBackgroundColorCached;
        Color     m_backgroundColorCached;
        Color     m_backgroundColorHoverCached;
        Color     m_selectedBackgroundColorCached;
        Color     m_selectedBackgroundColorHoverCached;
        Color     m_textColorCached;
        Color     m_textColorHoverCached;
        Color     m_selectedTextColorCached;
        Color     m_selectedTextColorHoverCached;

        /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    };

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#endif // TGUI_LIST_VIEW_HPP
